package com.xujl.hsns.client;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.SocketAddress;

import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.future.ReadFuture;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.core.session.IoSessionConfig;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.transport.socket.DefaultDatagramSessionConfig;
import org.apache.mina.transport.socket.nio.NioDatagramConnector;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.apache.mina.transport.vmpipe.VmPipeAddress;
import org.apache.mina.transport.vmpipe.VmPipeConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.xujl.hsns.carriers.Constants;
import com.xujl.hsns.carriers.Invoke;
import com.xujl.hsns.carriers.Result;
import com.xujl.hsns.codec.HSNSCodecFactory;
import com.xujl.hsns.codec.impl.JSONCodecFactory;
import com.xujl.hsns.codec.impl.JavaObjectCodecFactory;
import com.xujl.hsns.codec.impl.XMLCodecFactory;
import com.xujl.hsns.config.CODECER;
import com.xujl.hsns.config.PROTOCOL;
import com.xujl.hsns.test.TEST;

public class HSNSClient<T> {
	
	 private final static Logger LOGGER = LoggerFactory.getLogger(HSNSClient.class);
	 
	 private static long SconnectTimeoutMillis=6000L;
	 
	 private long connectTimeoutMillis=6000L;
	
	private SocketAddress socketAddress;
	
	private IoConnector connector;
	
	private ConnectFuture connFuture;
	
	private IoSession session;
	
	private HSNSCodecFactory codecFactory;
	
	private String ip;
	private int port;
	/**
	 *  UDP PROTOCOL and JSON CODECER
	 * @param ip
	 * @param port
	 */
	public HSNSClient(String ip,int port){
		this( ip, port,PROTOCOL.UDP ,SconnectTimeoutMillis,CODECER.JSON);
	}
	
	public HSNSClient(String ip,int port,PROTOCOL protocol,long connectTimeoutMillis,CODECER codecer){
	  	this.ip=ip;
	  	this.port=port;
		if(CODECER.JSON.equals(codecer)){
    		codecFactory= new JSONCodecFactory(Result.class); 
    	}else if(CODECER.XML.equals(codecer)){
    		codecFactory= new XMLCodecFactory(Result.class); 
    	}else{
    		codecFactory= new JavaObjectCodecFactory(); 
    	}
		this.connectTimeoutMillis=connectTimeoutMillis;
		socketAddress=new InetSocketAddress(ip,port);
		if(PROTOCOL.UDP.equals(protocol))
			udp();
		else if(PROTOCOL.TCP.equals(protocol))
			tcp();
		else{ 
			socketAddress= new VmPipeAddress(port);
			vmPipe();
		
		}

		
	}
	private void vmPipe() {
		
		connector = new VmPipeConnector();
		connector.setConnectTimeoutMillis(connectTimeoutMillis);
		IoSessionConfig sessionConfig = connector.getSessionConfig();
		sessionConfig.setMaxReadBufferSize(1024*1024*10);
		sessionConfig.setReadBufferSize(1024*1024*10);
		sessionConfig.setUseReadOperation(true);
		connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(codecFactory));
		connector.connect(socketAddress);
	}
	/**
	 */
	private void udp() {
		connector = new NioDatagramConnector();
		connector.setConnectTimeoutMillis(connectTimeoutMillis);
		DefaultDatagramSessionConfig sessionConfig = (DefaultDatagramSessionConfig)connector.getSessionConfig();
		sessionConfig.setMaxReadBufferSize(1024*1024*10);
		sessionConfig.setReadBufferSize(1024*1024*10);
		sessionConfig.setSendBufferSize(1024*1024*10);
		sessionConfig.setUseReadOperation(true);
		connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(codecFactory));
		connector.connect(socketAddress);
	}
	private void tcp() {
		connector = new NioSocketConnector();
		connector.setConnectTimeoutMillis(connectTimeoutMillis);
		IoSessionConfig sessionConfig = connector.getSessionConfig();
		sessionConfig.setMaxReadBufferSize(1024*1024*10);
		sessionConfig.setReadBufferSize(1024*1024*10);
		sessionConfig.setUseReadOperation(true);
		connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(codecFactory));
		connector.connect(socketAddress);
	}
	Object invoke(String method,Object[] arg) {


		activeSession();
	    Result message =null;
       try {
    	   
    	  Object object=Invoke.create(arg,method);
          // 发送
          session.write(object).awaitUninterruptibly();
          // 接收
          session.getConfig().setUseReadOperation(true);
          ReadFuture readFuture = session.read();
          if (readFuture.awaitUninterruptibly(this.connectTimeoutMillis)) {
        	  try {
				
        		  message = ( Result)readFuture.getMessage();
        		  LOGGER.debug("Result s:"+message.s);
			} catch (Exception e) {
				throw new Exception(e);
				
			}
             if(message.s == Constants.INVOKE_SATE_EX)throw new RuntimeException(message.ex);
            return message.v;
          } else {
              // 读超时
        	  throw new RuntimeException("网络超时");
          }
       }
       catch(Exception e){
    	e.printStackTrace();   
       } finally {
          // 断开
          //session.close(true);
       }
       	return message; 
	
		
	}
	
	
	public T getService(Class<T> serviceClass) {
		return (T)getProxy(serviceClass); 
	}
	public Object getServices() {
		return invoke("?", null); 
	}
	public Object getServiceMethods() {
		return invoke("/", null); 
	}
	

	
	private void  activeSession() {
		if(session==null || session.isClosing()){
			
			
			connFuture = connector.connect(socketAddress);
			connFuture.awaitUninterruptibly(connectTimeoutMillis);
		    session = connFuture.getSession();
		   // System.out.println("session reset connector "+connector.getConnectTimeoutMillis());
		}
	}
	public Object getProxy(Class<T> serviceClass) {
		
		return Proxy.newProxyInstance(serviceClass.getClassLoader(), new Class[] { serviceClass }, new HSNSProxy(this));
		
	}
	public void close() {
		if(session!=null)
			session.close(true);
		if(connector!=null)
			connector.dispose();
		
		
	}
	
	

}
